﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Reflection;
using System.Diagnostics;

namespace PJ.Framework
{
    /// <summary>
    /// Dummy interface for calling the dynamic method
    /// </summary>
    public interface IDynamicCalculator
    {
        /// <summary>
        /// Dynamicly created method
        /// </summary>
        /// <returns></returns>
        double eval();
    }

    /// <summary>
    /// Dynamic math (+ - / *) expression calculator
    /// </summary>
    public class ExpressionCalculator
    {
        /// <summary>
        /// Evaluate a simple math expression (+ - * /) 
        /// </summary>
        /// <param name="expression">the expression to evaluate</param>
        /// <param name="result">expression result</param>
        /// <returns>false if the calculator can't evaluate the expression else true</returns>
        public static bool Calc(string expression, out double result)
        {

            if (double.TryParse(expression, System.Globalization.NumberStyles.Number,
                    System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out result))
                return true;
            if (expression == null || expression == "")
            {
                result = 0;
                return false;
            }
            foreach (char c in expression)
            {
                if (!char.IsDigit(c) && c != '.') // Number
                    if (c != '+' && c != '-' && c != '/' && c != '*') // Operators
                        if (c != '(' && c != ')' && c != ' ')
                        {
                            result = 0;
                            return false;
                        }
            }
            CompilerParameters compileParams = new CompilerParameters(
                new string[] {  "system.dll", "PJ.Framework.Common.dll", 
                                    Process.GetCurrentProcess().MainModule.FileName});
            compileParams.GenerateInMemory = true;
            compileParams.GenerateExecutable = false;

            string src = @"using System; 
                                namespace PJ.Framework
                                {
                                    class DynamicCalculator : IDynamicCalculator
                                    {
                                         public double eval() { return " + expression + @"; }
                                    }
                                }";

            CompilerResults Compiled = CodeDomProvider.CreateProvider("CSharp").
                                        CompileAssemblyFromSource(compileParams, new string[] { src });


            if (Compiled.Errors.HasErrors)
            {
                result = 0;
                return false;
            }
            object obj = Compiled.CompiledAssembly.CreateInstance("PJ.Framework.DynamicCalculator");
            IDynamicCalculator instance = (IDynamicCalculator)(obj);
            result = instance.eval();
            return true;
        }

    }
}
